// -----------------------------------------------------------------------
//  <copyright file="ValidationBehaviour.cs" company="LiuliuSoft">
//      Copyright (c) 2022-2024 DaprPlus. All rights reserved.
//  </copyright>
//  <site>https://dapr.plus</site>
//  <last-editor>郭明锋</last-editor>
//  <last-date>2024/12/5 19:26:59</last-date>
// -----------------------------------------------------------------------

using FluentValidation;
using FluentValidation.Results;


namespace DaprPlus.MediatR.Behaviors;

public class ValidationBehaviour<TRequest, TResponse>(IEnumerable<IValidator<TRequest>> validators)
    : IPipelineBehavior<TRequest, TResponse>
    where TRequest : IRequest<TResponse>
{
    /// <summary>
    /// 管道处理程序。执行任何其他行为，并根据需要等待<paramref name="next"/>委托
    /// </summary>
    /// <param name="request">传入请求</param>
    /// <param name="next">管道中的下一个操作的可等待委托。最终，这个委托代表处理程序。</param>
    /// <param name="cancellationToken">取消令牌</param>
    /// <returns>等待任务返回 <typeparamref name="TResponse" /></returns>
    public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken cancellationToken)
    {
        if (!validators.Any())
        {
            return await next(cancellationToken);
        }

        var context = new ValidationContext<TRequest>(request);
        var validationResults =
            await Task.WhenAll(validators.Select(m => m.ValidateAsync(context, cancellationToken)));
        var failures = validationResults.SelectMany(m => m.Errors).Where(m => m != null).ToList();

        // 如果有validator校验失败，抛出异常，这里的异常是我们自定义的包装类型
        if (failures.Count > 0)
        {
            throw new ValidationException(GetValidationErrorMessage(failures));
        }

        return await next(cancellationToken);
    }

    // 格式化校验失败消息
    private static string GetValidationErrorMessage(IEnumerable<ValidationFailure> failures)
    {
        var failureDict = failures
            .GroupBy(e => e.PropertyName, e => e.ErrorMessage)
            .ToDictionary(failureGroup => failureGroup.Key, failureGroup => failureGroup.ToArray());

        return string.Join(";", failureDict.Select(kv => kv.Key + ": " + string.Join(' ', kv.Value.ToArray())));
    }
}
